package com.wcq.studentbackend.util; // 或您选择的其他包名

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class JwtUtil {

    // Token 有效期（例如：1小时）ms
    public static final long JWT_TOKEN_VALIDITY = 60 * 60 * 1000; 

    // 从 application.properties (或 .yml) 文件中读取密钥
    // 强烈建议将此密钥配置在外部，而不是硬编码在代码中
    // 例如： jwt.secret=your-very-secure-and-long-secret-key-that-is-at-least-256-bits
    @Value("${jwt.secret:adaudoaidjascnaslncoiaicosacnioasnmcjascm}") // 提供一个默认值仅用于开发
    private String secretString;

    private SecretKey getSigningKey() {
        // 确保密钥长度足够用于 HS256 算法 (至少256位)
        // 如果 secretString 太短, Keys.hmacShaKeyFor 会抛异常，或使用不安全的密钥大小
        // 对于 HS256, 密钥长度应至少 32 bytes (256 bits).
        // 一个简单的做法是确保配置的 secretString 足够长，或者对其进行哈希处理以获得固定长度。
        // 这里我们直接使用 UTF-8 编码的字节，实际项目中应更注意密钥管理。
        if (secretString == null || secretString.length() < 32) {
            // Log a warning or throw an exception in a real application
            System.err.println("Warning: JWT secret key is too short or null. Using a default insecure key. THIS IS NOT FOR PRODUCTION.");
            // Provide a default, sufficiently long key ONLY for situations where the configured one is bad AND it's not production
            // This is still not ideal, as it's a known default.
            return Keys.hmacShaKeyFor("DefaultInsecureSecretKeyForTestingPurposesPleaseChangeThisImmediately".getBytes(StandardCharsets.UTF_8));
        }
        return Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
    }

    // 从 token 中获取用户名
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    // 从 token 中获取过期时间
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    // 从 token 中获取指定的 claim
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    // 获取 token 中的所有 claims
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
    }

    // 检查 token 是否过期
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    // 为用户生成 token
    public String generateToken(UserDetails userDetails, String actualRole) {
        Map<String, Object> claims = new HashMap<>();
        List<String> rolesFromAuthorities = userDetails.getAuthorities().stream()
                                .map(GrantedAuthority::getAuthority)
                                .collect(Collectors.toList());
        claims.put("roles", rolesFromAuthorities);
        // subject 应该是 UserDetailsServiceImpl.loadUserByUsername 所期望的格式
        String subjectWithRole = actualRole.toLowerCase() + ":" + userDetails.getUsername();
        return doGenerateToken(claims, subjectWithRole);
    }

    // 创建 token 的具体逻辑
    private String doGenerateToken(Map<String, Object> claims, String subjectWithRole) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subjectWithRole) // subject 现在是 "role:identifier"
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    // 验证 token (现在主要检查是否过期，因为用户名已被用于加载 UserDetails)
    public Boolean validateToken(String token, UserDetails userDetails) {
        // UserDetails userDetails 参数现在主要用于保持接口的一致性，
        // 之前的逻辑 (usernameFromToken.equals(userDetails.getUsername())) 是错误的，因为
        // userDetails.getUsername() 返回的是原始 identifier ("admin"),
        // 而 getUsernameFromToken(token) 返回的是 "role:identifier" ("admin:admin").
        //
        // 只要我们能从 token 中成功提取出 subject (getUsernameFromToken -> getAllClaimsFromToken 会验证签名)，
        // 并且用这个 subject 成功加载了 userDetails (这在 JwtRequestFilter 中完成)，
        // 那么我们在这里主要关心的就是 token 是否过期。
        try {
            // 确保 token 的 subject 与 UserDetails 的 username (现在是 identifier) 相关联的 "role:identifier" 匹配
            // 这一步其实在 JwtRequestFilter 中已经通过 UserDetailsServiceImpl.loadUserByUsername(usernameFromToken) 隐式完成了
            // 如果能加载出 userDetails，说明 usernameFromToken 是有效的用户标识。
            // 因此，这里主要检查过期。
            return !isTokenExpired(token);
        } catch (Exception e) {
            // 例如，如果 token 格式错误导致 isTokenExpired 内部解析失败
            System.err.println("Error during token validation (isTokenExpired check): " + e.getMessage());
            return false;
        }
    }

    // (可选) 仅验证 token 是否有效（不对比用户名，适用于某些场景）
    public Boolean validateTokenSignature(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);
            return !isTokenExpired(token); // 签名有效，再检查是否过期
        } catch (Exception e) { // Jwts.parser 会在签名无效、格式错误、过期等情况下抛出异常
            // Log.error("Token validation error: {}", e.getMessage());
            return false;
        }
    }

    // 静态方法：从 token 获取 subject
    public static String getUsernameFromTokenStatic(String token) {
        // 这里直接用 jjwt 解析，需与 getSigningKey 保持一致
        // 注意：如有多环境密钥管理，建议优化
        String secret = "adaudoaidjascnaslncoiaicosacnioasnmcjascm";
        try {
            io.jsonwebtoken.Claims claims = io.jsonwebtoken.Jwts.parserBuilder()
                .setSigningKey(io.jsonwebtoken.security.Keys.hmacShaKeyFor(secret.getBytes(java.nio.charset.StandardCharsets.UTF_8)))
                .build()
                .parseClaimsJws(token)
                .getBody();
            return claims.getSubject();
        } catch (Exception e) {
            throw new RuntimeException("Token解析失败: " + e.getMessage(), e);
        }
    }
}